home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Workbench Design
/
WB Collection.iso
/
workbench werkzeuge
/
scherz programme
/
clicker
/
source
/
sound.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-04-07
|
7KB
|
215 lines
/* File: sound.c
* Created: 20-10-95
* Updated: 30-12-95
* Version: 1.0
* Project: Clicker
* Owner: Jeroen Vermeulen
* Requirements: KickStart V39+
* Legal: PD
* Status: Release
*/
#include <math.h>
#include <proto/exec.h>
#include <exec/devices.h>
#include <exec/memory.h>
#include <devices/audio.h>
#include <proto/alib.h>
#include "main.h"
#include "sound.h"
#include "prefs.h"
static STRPTR
AllocFailChipMem = "Not enough CHIP memory!\n",
AllocFailPubMem = "Not enough PUBLIC memory!\n",
OpenFailAudioDev = "Can't open audio.device\n";
/* Here's our basic built-in sample. It's a very crude sine wave but it should
* do for those very short intervals.
* NOTE: SAMPLELENGTH must be defined to the length of this array!
*/
static signed char Sample[SAMPLELENGTH] = { 0, 60, 100, 127, 100, 60,
0, -60, -100, -127, -100, -60 };
/* Allocation priority array for sound channels. Don't see the point really, as
* we're only asking for a single lousy sound channel. Oh well, audio.device
* seems to insist that we provide this.
*/
static unsigned char SoundChannels[] = { 2,1,4,8 };
/* CreateSample():
* Open audio device and sample. An IOAudio structure is returned unless either
* an error occurs or the error string was already non-NULL before the call.
*/
struct IOAudio *CreateSample(STRPTR *const error)
{
struct IOAudio *soundrequest = NULL;
struct IORequest *plainrequest;
struct MsgPort *Reply;
if ((Reply = CreateMsgPort()))
{
plainrequest = CreateIORequest(Reply,sizeof(struct IOAudio));
soundrequest = (struct IOAudio *)plainrequest;
if (soundrequest)
{
soundrequest->ioa_Data = SoundChannels;
soundrequest->ioa_Length = 4;
plainrequest->io_Command = ADCMD_ALLOCATE;
plainrequest->io_Flags = ADIOF_NOWAIT;
if (OpenDevice("audio.device",0,plainrequest,0) == 0)
{
UBYTE *SampleMem;
/* --- */
SampleMem = (UBYTE *)AllocMem(SAMPLELENGTH,MEMF_PUBLIC|MEMF_CHIP);
if (SampleMem)
{
CopyMem(Sample,SampleMem,SAMPLELENGTH);
plainrequest->io_Flags = ADIOF_PERVOL|IOF_QUICK;
plainrequest->io_Command = CMD_WRITE;
soundrequest->ioa_Data = SampleMem;
soundrequest->ioa_Length = SAMPLELENGTH;
}
else *error = AllocFailChipMem;
}
else *error = OpenFailAudioDev;
}
else *error = AllocFailPubMem;
}
else *error = AllocFailMsgPort;
if (*error)
{
DeleteSample(soundrequest);
soundrequest = NULL;
}
return soundrequest;
}
/* Destroy sample and free all resources allocated with it. This function is
* overly robust so it can be used from within CreateSample() in case of an
* error.
*/
void DeleteSample(struct IOAudio *const soundrequest)
{
if (soundrequest)
{
struct IORequest *const plainrequest = &soundrequest->ioa_Request;
if (plainrequest->io_Message.mn_ReplyPort)
{
CloseDevice(plainrequest);
DeleteMsgPort(plainrequest->io_Message.mn_ReplyPort);
}
if (soundrequest->ioa_Data) FreeMem(soundrequest->ioa_Data,SAMPLELENGTH);
DeleteIORequest(plainrequest);
}
}
/* KeyClick():
* Make key-click noise. The soundrequest pointer is assumed to be valid and
* non-NULL, and point at a properly initialized IOAudio structure.
*/
void KeyClick(struct IOAudio *const soundrequest)
{
if (ClickPrefs.newsettings)
{
ClickPrefs.newsettings = FALSE;
soundrequest->ioa_Period = ClickPrefs.period;
soundrequest->ioa_Volume = ClickPrefs.volume;
soundrequest->ioa_Cycles = ClickPrefs.cycles;
soundrequest->ioa_Request.io_Flags = ADIOF_PERVOL | IOF_QUICK;
}
BeginIO(&soundrequest->ioa_Request);
WaitIO(&soundrequest->ioa_Request);
}
/* SliderToHertz():
* Converts a prefs window slider position (between -5*12 and 4*12) to a
* frequency in Hertz, based on a twelve-tone octave centered at the 440 Hz A.
*/
LONG SliderToHertz(const struct Gadget *const dum, const WORD sliderpos)
{
/* Frequency is 440 * 2^(n/12). Note that left-shift cannot be used here.
*/
return (LONG)(440 * pow((double)2,sliderpos/(double)12));
}
/* HertzToPeriod():
* Converts human-readable pitch in Hertz to period length suitable for use by
* audio.device. As a rule, HertzToPeriod(SliderToHertz(S)) is equivalent to
* SliderToPeriod(S).
*/
UWORD HertzToPeriod(const LONG Hertz)
{
/* Period should be computed from frequency f as 10^9 / (279.365 * s * f),
* where s is the sample length. With proper constant folding, this should
* compile to (C/Hertz) where C is a constant.
*/
return (UWORD)((1000000000/(279.365*SAMPLELENGTH))/Hertz);
}
/* PeriodToHertz():
* Converts audio.device period length to human-readable pitch in Hertz. This
* is the inverse of HertzToPeriod().
*/
LONG PeriodToHertz(const UWORD period)
{
/* Pitch in Hertz is computed from period f as 279.365 * s * p / 10^9, where s
* is sample length.
*/
return (LONG)((279.365 * SAMPLELENGTH / 1000000000) * period);
}
/* SliderToPeriod():
* Converts a prefs window slider position (between 0 and 9*12) to a period
* length (in units of 279.365 nanoseconds) suitable for use by audio.device.
*/
UWORD SliderToPeriod(const WORD sliderpos)
{
/* For a frequency f, the period length is 10^9 / (279.365 * s * f). Here s
* is the length of the waveform sample (SAMPLELENGTH).
* So we need to compute 10^9 / (279.365 * s * (440 * 2^(n/12)))
* == 2^(-n/12) * 10^9 / (279.365 * 440 * s)
* == 2^(-n/12) * 10^9 / (122920.6 * s)
*/
return (UWORD)(pow((double)2,-sliderpos/(double)12) *
((double)10000000 / (1229.206 * SAMPLELENGTH)));
}
/* PeriodToSlider():
* Inverse of SliderToPeriod(). Takes a period length as an argument and
* computes the appropriate slider position (between -5*12 and 4*12). This
* function can afford to be slow because it's only ever called when the prefs
* window pops up.
*/
WORD PeriodToSlider(const UWORD period)
{
/* Since the period number p can be computed from slider position n by
* p = 10^9 / (279.365 * 440 * s * 2^(n/12)) <==>
* 2^(n/12) = 10^9 / (279.365 * 440 * s * p) <==>
* n/12 = 2log(10^9 / (279.365 * 440 * s * p)) <==>
* n = 12 * 2log(10^9 / (279.365 * 440 * s * p)) ==
* 12 * 2log(10^9 / (122920.6 * s * p)) ==
* 12 * ln(10^9 / (122920.6 * s * p)) / ln(2)
*/
return (WORD)(12 * log(
(double)1000000000 / (122920.6*SAMPLELENGTH*period)
)/log((double)2));
}